home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-06-24 | 24.3 KB | 829 lines | [TEXT/MPS ] |
- /*
- File: FractalMain.c
-
- Used to build: “Fractal 8”
-
- Written by: Jim Cathey July 1985
- Eric Traut November 1994
-
- Description:
- The following code implements a “Fractal Contour” generating
- program. The program was originally written in Microsoft BASIC
- and ported to Aztec C in 1985 by Jim Cathey. It has been a
- widely-distributed public domain application since that time.
-
- The program generates a fractal surface by starting with a
- triangular surface. It then subdivides the triangle into four
- subtriangles by calculating the midpoint of each edge. The
- Z coordinate for each of these edges is then randomly incremented
- or decremented. This processes is repeated for each of the
- subtrianges until a jagged fractal surface results.
-
- The original program uses a “cordic” function to approximate
- the calculation of various transcendental functions used in
- three-dimensional graphics transforms.
-
- In 1994, Eric Traut modified the code to run under System 7.5.
- The program was also updated so it could be recompiled under
- PowerPC. This source is part of a series of modifications made
- to demonstrate code optimizations under PowerPC.
-
- ------------------------------------------------------------
-
- The following documents the changes made to the program at
- each step of the optimization process and lists the speed
- as measured under a PowerMac 8100/80av (8-bit 13"monitor).
- All stages were compiled using Metrowerks’ CodeWarrier
- 4.0 compilers.
-
-
- • STAGE 1:
- 0.50 fractals per second
-
- The first step to optimizing for PowerPC is to clean up
- the source code so it compiles under today’s 68K compilers and
- runs under today’s system software. I also added a feature
- to allow repeated calculation of fractals and a display
- of “fractals per second.” This value will be used to measure
- our progress as we further optimize the program. Note that
- this version runs emulated on a PowerMac.
-
- • STAGE 2:
- 1.92 fractals per second
- 3.84x emulated speed
-
- This version was created by simply recompiling stage 1 with the
- PowerPC MetroWerks CodeWarrier C compiler. No other changes
- were made.
-
- • STAGE 3:
- 2.86 fractals per second
- 5.72x emulated speed
- 1.49x original native speed
-
- This version was uses an off-screen GWorld for all drawing. This
- naturally uses more memory, but QuickDraw can generally draw to
- an off-screen GWorld faster because of caching characteristics
- (the video buffer is mapped cache-write-through whereas the rest
- of RAM is mapped cache-write-back). The program then uses
- QuickDraw’s CopyBits routine to copy the image to the screen.
-
- • STAGE 4:
- 3.32 fractals per second
- 6.64x emulated speed
- 1.73x original native speed
- 1.16x previous stage
-
- This version replaces QuickDraw’s LineTo with custom offscreen
- line drawing functions. These functions ignore many of the complex
- factors which QuickDraw considers including clipping, color mapping,
- and patterns. Note that bypassing QuickDraw should not be used as
- a general solution for improving performance, but for small specialized
- portions of an app it is OK. Note that this code may run faster under
- a graphics accellerator if QuickDraw were not bypassed.
-
- • STAGE 5:
- 6.00 fractals per second
- 12.00x emulated speed
- 3.13x original native speed
- 1.81x previous stage
-
- This version removes a call from the QuickDraw call “GetPixBaseAddr”
- from the DoPlot routine. This routine is emulated on the first
- PowerMacs, so it should be called as infrequently as possible. In
- our case, we do not need to call it in such a tight loop. By moving
- it to the PlotData function, we save considerable time. This version
- also removes the calls to SetCursor to display the busy cursor. We
- are now drawing fast enough that it makes no sense to display it.
-
- • STAGE 6:
- 10.00 fractals per second
- 20.00x emulated speed
- 5.21x original native speed
- 1.67x previous stage
-
- This version uses slightly modified routines for caculating and
- plotting fractal surfaces. In general, short variables were changed
- to long. Divisions are avoided wherever possible. All local functions
- are defined as static. Points are drawn with a special function
- instead of going through the more complex LineTo function. Scaling
- is done with shifts rather than multiplications and divisions. This
- results in a much better performance, but the scaling is not as
- smooth.
-
- • STAGE 7:
- 21.27 fractals per second
- 42.54x emulated speed
- 11.08x original native speed
- 2.13x previous stage
-
- This version replaces the previous method’s scaling algorithms.
- Instead of using the cordic algorithm to perform graphical
- transforms, it uses a simpler linear algebra strategy. This
- results in much faster calculation. To further improve speed,
- this version allocates a second plot point array to record X
- and Y screen coordinates as well as scaled Z coordinates.
- Previous versions recalculated the transforms for each point
- three times -- once when plotting X, Y, and dialognal lines.
- The line drawing routines in this version are also further
- optimized.
-
- • STAGE 8:
-
- This version has no performance improvements over the previous
- stage. Instead, it was written to make a point about performance.
- Once the program speed is sufficient, programmers should try to
- innovate and use the added speed to improve the user interface
- or add some other feature which was previously not possible.
- In this case, I added two new features. The first is a “morphing”
- feature which draws smooth transitions between consequtive fractals.
- This slows down fractal computation but produces a very interesting
- animation effect. The second new feature allows the user to select
- between a wire frame mode (the only option in previous stages) and
- a surface mode. The surface mode draws the complete shaded surface
- by filling in each triangle with a solid color. Thanks to Dan
- Clifford for the tripatch blitting code.
-
- */
-
- #include "SenderSetup.h"
-
- #pragma cplusplus off
-
- #include <Types.h>
- #include <Memory.h>
- #include <Quickdraw.h>
- #include <Fonts.h>
- #include <Windows.h>
- #include <OSUtils.h>
- #include <Menus.h>
- #include <Events.h>
- #include <TextEdit.h>
- #include <Dialogs.h>
- #include <Desk.h>
- #include <Controls.h>
- #include <ToolUtils.h>
- #include <Resources.h>
- #include <Strings.h>
- #include <QDOffscreen.h>
- #include <Timer.h>
- #include <SegLoad.h>
-
- #include <stdio.h>
- #include <math.h>
-
- #include "Fractal.h"
-
- /* Functions defined within the file */
- void InitApp(void);
- void SetUpMenus(void);
- void SetUpWindow(void);
- void MainEventLoop(void);
- void UpdateWindow(WindowPtr theWindow);
- void DrawTimeInfo(void);
- Boolean DoMenuCommand(long mresult);
- void DoSetUpDialog(void);
- void SetTerrainButton(DialogPtr ptr, short oncontrl, short offcontrl);
- void DoAboutBox(void);
- void ReportFatalError(void);
- void CleanUpApp(void);
- void ClearPointArrays(void);
- void CalculateShadeMap(void);
-
- /* Global variables */
- short (*gOldPointArray)[kMaxXPoint][kMaxYPoint] = NULL; /* The array of points to be subdivided */
- /* The array of points to be subdivided */
- short (*gNewPointArray)[kMaxXPoint][kMaxYPoint] = NULL; /* The array of points to be subdivided */
- /* The array of points to be subdivided */
- ThreePoint (*gPlotPointArray)[2 * kMaxXPoint][2 * kMaxYPoint] = NULL; /* The array of points to be subdivided */
- /* The array of points to be subdivided */
- short gContourType; /* Contour type */
- short gContourLevel; /* Level of detail */
- WindowPtr gMainWindow = NULL; /* Our one window */
- short gMainWindowHeight; /* Height of main window */
- short gMainWindowWidth; /* Width of main window */
- Boolean gContinuousRedraw; /* Continuously redraw fractal */
- long gMicrosecondCount; /* Total time spent calculating and drawing */
- long gTotalFractals; /* Total fractal count */
- Boolean gFractalChanged; /* Has fractal changed since last update? */
- GWorldPtr gOffscreenGWorld = NULL; /* GWorld for faster drawing */
- PixMapHandle gOffscreenPixMap = NULL; /* GWorld’s Pix Map */
- float gMorphAmount; /* Percent of morph complete */
- float gMorphAmountNeg; /* 1 - above value */
- Boolean gPlotSurface; /* Plot surface or wire frame? */
- Boolean gMorphFractal = false; /* Morph or not morph */
- unsigned short gShadeMap[kShadeMapSize]; /* Mapping of grays */
-
-
- /*
- main
- */
- void main(void)
- {
- InitApp();
- MainEventLoop();
- CleanUpApp();
- }
-
-
- /*
- CleanUpApp
- */
- void CleanUpApp(void)
- {
- CleanUpSender(); //•• ES
-
- if (gOffscreenGWorld)
- DisposeGWorld(gOffscreenGWorld);
-
- if (gMainWindow)
- DisposeWindow(gMainWindow);
-
- if (gOldPointArray)
- DisposePtr((Ptr)gOldPointArray);
-
- if (gNewPointArray)
- DisposePtr((Ptr)gNewPointArray);
-
- if (gPlotPointArray)
- DisposePtr((Ptr)gPlotPointArray);
-
- ExitToShell();
- }
-
-
- /*
- MainEventLoop
- */
- void MainEventLoop(void)
- {
- EventRecord curEvent; /* Event we should respond to */
- WindowPtr whichWindow; /* Window which received the click */
- Boolean userDone; /* Should we quit the program? */
-
- userDone = false;
-
- /* Get next event, and handle it appropriately, until user quits */
- while (!userDone) {
-
- SetPort(gMainWindow);
- if (WaitNextEvent(everyEvent, &curEvent, 1, NULL)) {
- switch (curEvent.what) {
- case mouseDown:
- switch (FindWindow(curEvent.where, &whichWindow)) {
- case inSysWindow: /* handle the desk accessories */
- SystemClick(&curEvent, whichWindow);
- break;
- case inMenuBar: /* handle the command */
- userDone = DoMenuCommand(MenuSelect(curEvent.where));
- break;
- case inDrag: /* No Drag region, treat as content */
- {
- Rect limitRect;
-
- limitRect = qd.screenBits.bounds;
- InsetRect(&limitRect, 4, 4);
- DragWindow(gMainWindow, curEvent.where, &limitRect);
- break;
- }
- case inContent: /* Activate window */
- if (whichWindow == gMainWindow)
- if (whichWindow != FrontWindow())
- SelectWindow(whichWindow);
- break;
- case inGrow: /* No Grow Region */
- case inGoAway: /* We don’t have a GoAway region */
- break;
- }
- break;
-
- case keyDown:
- case autoKey: /* If command key, pass the char to MenuKey */
- if (curEvent.modifiers & cmdKey)
- userDone = DoMenuCommand(MenuKey((char)(curEvent.message & charCodeMask)));
- break;
- case updateEvt: /* If it’s for our window, update it */
- if ((WindowPtr)curEvent.message == gMainWindow)
- UpdateWindow(gMainWindow);
- break;
- case activateEvt: /* If for our window, set port as necessary */
- if ((WindowPtr)curEvent.message == gMainWindow) {
- if (curEvent.modifiers & 1) {
- /* odd means an activate event */
- SetPort(gMainWindow);
- DisableItem(GetMenu(kEditMenuID), 0);
- EnableItem(GetMenu(kFileMenuID), 0);
- EnableItem(GetMenu(kOptionsMenuID), 0);
- DrawMenuBar();
- }
- else {
- EnableItem(GetMenu(kEditMenuID), 0);
- DisableItem(GetMenu(kFileMenuID), 0);
- DisableItem(GetMenu(kOptionsMenuID), 0);
- DrawMenuBar();
- }
- }
- break;
- }
- }
- else {
- if (gContinuousRedraw) {
- if (gMorphFractal) {
- if (gMorphAmount >= 1.0) {
- void* tempPtr;
-
- tempPtr = gNewPointArray;
- gNewPointArray = gOldPointArray;
- gOldPointArray = tempPtr;
-
- CalcSurface(gContourLevel);
- }
- else {
- /* Morphing is too fast for levels under 7, so we will
- hold it back by creating more intermediate frames */
- if (gContourLevel == kMaxLevel)
- gMorphAmount += 0.2;
- else
- gMorphAmount += 0.1;
- gFractalChanged = true;
- InvalRect(&gMainWindow->portRect);
- UpdateWindow(gMainWindow);
- }
- }
- else {
- gMorphAmount += 1.0;
- CalcSurface(gContourLevel);
- InvalRect(&gMainWindow->portRect);
- UpdateWindow(gMainWindow);
- }
- }
- }
- }
- }
-
-
- /*
- DoMenuCommand
-
- This function responds to the menu command returned by MenuSelect.
- If it was Quit, we return true, else false. Since the menu was
- highlighted by MenuSelect, we must finish by unhighlighting it
- to indicate we're done.
- */
- Boolean DoMenuCommand(long menuResult)
- {
- short menuID; /* menu ID of selected menu */
- short itemNumber; /* item number of selected menu item */
- Boolean quitSelected; /* was menu item Quit? */
-
- quitSelected = false; /* Assume Quit not selected */
- menuID = HiWord(menuResult); /* Get the menu selected */
- itemNumber = LoWord(menuResult); /* ... and the item of that menu */
-
- switch (menuID) {
- case kAppleMenuID:
- if (itemNumber == kAboutBoxItem) /* Tell about FracCont */
- DoAboutBox();
- else { /* Run a desk accessory */
- Str255 menuName;
- GrafPtr savedPort;
-
- GetPort(&savedPort); /* Preserve port */
- GetMenuItemText(GetMenu(kAppleMenuID), itemNumber, menuName);
- (void) OpenDeskAcc(menuName); /* Run the desk accessory */
- SetPort(savedPort); /* Restore port */
- }
- break;
-
- case kFileMenuID:
- switch (itemNumber) {
- case kNewFractalItem: /* New Surface */
- CalcSurface(gContourLevel);
- InvalRect(&gMainWindow -> portRect);
- break;
- case kQuitItem:
- quitSelected = true; /* Quit */
- break;
- }
- break;
-
- case kOptionsMenuID:
- switch (itemNumber) {
- case kSetupItem:
- DoSetUpDialog();
- break;
- case kContinuousItem:
- SetItemMark(GetMenu(kOptionsMenuID), kContinuousItem, gContinuousRedraw ?
- noMark : checkMark);
- gContinuousRedraw = !gContinuousRedraw;
- if (gContinuousRedraw)
- gMorphAmount = 0.0;
- else
- gMorphAmount = 1.0;
- break;
- case kMorphItem:
- SetItemMark(GetMenu(kOptionsMenuID), kMorphItem, gMorphFractal ?
- noMark : checkMark);
- gMorphFractal = !gMorphFractal;
- gFractalChanged = true;
- InvalRect(&gMainWindow->portRect);
- break;
- case kDrawSurfaceItem:
- SetItemMark(GetMenu(kOptionsMenuID), kDrawSurfaceItem, gPlotSurface ?
- noMark : checkMark);
- gPlotSurface = !gPlotSurface;
- gFractalChanged = true;
- InvalRect(&gMainWindow->portRect);
- break;
- case kDrawSurfaceItem+2: //•• ES: yes this is ugly
- SpawnSender();
- break;
- }
- break;
- }
-
- HiliteMenu(0); /* Turn off hilighting on the menu just used */
- return quitSelected;
- }
-
-
- /*
- SetTerrainButton
-
- This function is called by the DoSetUpDialog function to change
- the current terrain button.
- */
- void SetTerrainButton(DialogPtr theDialog, short newControlID, short oldControlID)
- {
- short itemType;
- ControlHandle theControl;
- Rect itemBox;
-
- GetDialogItem(theDialog, oldControlID, &itemType, (Handle*)&theControl, &itemBox);
- SetControlValue(theControl, 0); /* Turn off old default button */
- GetDialogItem(theDialog, newControlID, &itemType, (Handle*)&theControl, &itemBox);
- SetControlValue(theControl, 1); /* Turn on default button */
- }
-
-
- /*
- DoSetUpDialog
-
- This function displays the set-up dialog and handles user actions
- while the dialog is up.
- */
- void DoSetUpDialog(void)
- {
- DialogPtr theDialog; /* Pointer to dialog */
- long newType; /* Selected contour type */
- long newLevel; /* Selected level of detail */
- Boolean doAnother; /* True if we need to recalc */
- short itemHit = 0; /* Item number of selected item */
- Str255 itemText; /* Used for converting string to num */
- short itemType; /* Dummy variable needed for GetDialogItem */
- Handle itemHandle; /* Item handle returned by GetDialogItem */
- Rect itemBox; /* Item bound box returned by GetDialogItem */
-
- newType = gContourType; /* Initialize local vars to current values */
- newLevel = gContourLevel;
- doAnother = false;
-
- /* Display the dialog and set initial values if controls */
- theDialog = GetNewDialog(kSetUpDialogID, NULL, (WindowPtr) -1);
- SetPort(theDialog);
-
- SetTerrainButton(theDialog, gContourType, gContourType);
- NumToString(newLevel, itemText);
- GetDialogItem(theDialog, kSetUpLevelID, &itemType, &itemHandle, &itemBox);
- SetDialogItemText(itemHandle, itemText);
- SelectDialogItemText(theDialog, kSetUpLevelID, 0, 32767); /* Hilite entire text field. */
-
- while (itemHit != kSetUpOKButtonID && itemHit != kSetUpCancelButtonID) {
- ModalDialog(NULL, &itemHit);
- GetDialogItem(theDialog, kSetUpLevelID, &itemType, &itemHandle, &itemBox);
- GetDialogItemText(itemHandle, itemText); /* Get Level field */
- StringToNum(itemText, &newLevel);
-
- /* Check for legal contour level */
- if (newLevel < 1 || newLevel > kMaxLevel) {
- SysBeep(0);
- newLevel = gContourLevel;
- NumToString(newLevel, itemText);
- GetDialogItem(theDialog, kSetUpLevelID, &itemType, &itemHandle, &itemBox);
- SetDialogItemText(itemHandle, itemText);
- SelectDialogItemText(theDialog, kSetUpLevelID, 0, 100); /* Hilite text field. */
- }
-
- if (itemHit == kSetUpMtnButtonID ||
- itemHit == kSetUpHillsButtonID ||
- itemHit == kSetUpWaterButtonID) {
- SetTerrainButton(theDialog, itemHit, newType);
- newType = itemHit;
- }
-
- if (itemHit == kSetUpOKButtonID && ((newLevel > 0) & (newLevel <= kMaxLevel))) {
- if (gContourType != newType || gContourLevel != newLevel) {
- ClearPointArrays();
- doAnother = true;
- SetPort(gMainWindow);
- InvalRect(&gMainWindow->portRect);
- gContourLevel = newLevel;
- gContourType = newType;
- }
- }
- }
-
- DisposeDialog(theDialog); /* Release storage and remove dialog from screen */
-
- if (doAnother) {
- gMicrosecondCount = 0;
- gTotalFractals = 0;
- CalcSurface(newLevel);
- InvalRect(&gMainWindow->portRect);
- UpdateWindow(gMainWindow);
- }
- }
-
-
- /*
- DoAboutBox
-
- This function displays the about box dialog.
- */
- void DoAboutBox(void)
- {
- short itemHit;
- DialogPtr theDialog;
-
- theDialog = GetNewDialog(kAboutBox1DialogID, NULL, (WindowPtr) -1);
- if (theDialog == NULL)
- ReportFatalError();
-
- SetPort(theDialog);
-
- ModalDialog(NULL, &itemHit);
- DisposeDialog(theDialog);
-
- if (itemHit == kAboutBoxMoreButtonID) {
- theDialog = GetNewDialog(kAboutBox2DialogID, NULL, (WindowPtr) -1);
- ModalDialog(NULL, &itemHit);
- DisposeDialog(theDialog);
- }
- }
-
-
- /*
- SetUpMenus
-
- This function initilizes the menu bar and the applications menus.
- */
- void SetUpMenus(void)
- {
- SetMenuBar(GetNewMBar(kMenuBarID));
- AppendResMenu(GetMenu(kAppleMenuID), 'DRVR');
- DrawMenuBar();
- }
-
-
- /*
- SetUpWindow
-
- This function initializes the main window and its associated
- global variables. It also sets up the offscreen GWorld for
- our drawing and screen updates.
- */
- void SetUpWindow(void)
- {
- GDHandle mainDeviceHandle;
- short maxWindowHeight, maxWindowWidth;
- Rect offscreenRect;
-
- gMainWindow = GetNewCWindow(kMainWindowID, NULL, (WindowPtr)-1);
- mainDeviceHandle = GetMainDevice();
-
- maxWindowHeight = (*mainDeviceHandle)->gdRect.bottom - (*mainDeviceHandle)->gdRect.top -
- (2 * kScreenBoundaryBits) - GetMBarHeight() - kWindowTitleHeight;
- maxWindowWidth = (*mainDeviceHandle)->gdRect.right - (*mainDeviceHandle)->gdRect.left -
- (2 * kScreenBoundaryBits);
-
- if (maxWindowHeight >= kNewScreenY && maxWindowWidth >= kNewScreenX) {
- gMainWindowHeight = kNewScreenY;
- gMainWindowWidth = kNewScreenX;
- }
- else {
- gMainWindowHeight = maxWindowHeight;
- gMainWindowWidth = maxWindowWidth;
- }
-
- SizeWindow(gMainWindow, gMainWindowWidth, gMainWindowHeight, false);
- MoveWindow(gMainWindow,
- (*mainDeviceHandle)->gdRect.left + kScreenBoundaryBits,
- (*mainDeviceHandle)->gdRect.top + kScreenBoundaryBits + GetMBarHeight() + kWindowTitleHeight,
- false);
-
- /* Now, allocate the offscreen GWorld */
- SetRect(&offscreenRect, 0, 0, kNewScreenX, kNewScreenY);
- (void) NewGWorld(&gOffscreenGWorld, 8, &offscreenRect, NULL, mainDeviceHandle, 0);
-
- if (gOffscreenGWorld == NULL)
- ReportFatalError();
-
- if (gOffscreenGWorld)
- gOffscreenPixMap = GetGWorldPixMap(gOffscreenGWorld);
-
- ShowWindow(gMainWindow);
- }
-
-
- /*
- CalculateShadeMap
-
- This function calculates the pixel values for a range of
- grays. It does this by calculating the amount of light
- reflected from a given triangular patch if we assume
- a white light positioned directly above.
- */
- void CalculateShadeMap()
- {
- long count;
-
- for (count = 0; count < kShadeMapSize; count++) {
-
- double vLength;
-
- vLength = sqrt(kVectorBaseSize * kVectorBaseSize + (count * count));
-
- gShadeMap[count] = (kTotalGrays - 1) - ((count * kTotalGrays) / vLength);
-
- if (gShadeMap[count] < 2)
- gShadeMap[count] = 2;
-
- if (gShadeMap[count] > kTotalGrays - 1)
- gShadeMap[count] = kTotalGrays - 1;
- }
- }
-
-
- /*
- InitApp
-
- This function initializes the program the Mac toolbox.
- */
- void InitApp(void)
- {
- InitGraf(&qd.thePort);
- InitFonts();
- InitWindows();
- InitMenus();
- TEInit();
- InitDialogs(NULL);
- InitCursor();
-
- InitSender(); //•• ES
-
- gContinuousRedraw = false;
- gPlotSurface = false;
- gMicrosecondCount = 0; /* Initialize performance counters */
- gTotalFractals = 0;
-
- SetUpMenus(); /* Set up our menus */
- gContourType = kDefaultStyle; /* Default style */
- gContourLevel = kDefaultLevel; /* Default Level */
-
- /* Allocate the data arrays */
- gOldPointArray = (void*)NewPtr((long) kMaxXPoint*kMaxYPoint*(sizeof(short)));
- gNewPointArray = (void*)NewPtr((long) kMaxXPoint*kMaxYPoint*(sizeof(short)));
- gPlotPointArray = (void*)NewPtr((long) (2 * kMaxXPoint)*(kMaxYPoint * 2)*(sizeof(ThreePoint)));
-
- /* Make sure we got some memory */
- if (gOldPointArray == NULL || gNewPointArray == NULL || gPlotPointArray == NULL)
- ReportFatalError();
-
- ClearPointArrays();
-
- SetUpWindow(); /* Create new window */
- CalculateShadeMap();
-
- CalcSurface(gContourLevel); /* Do at least one first. */
- }
-
-
- /*
- UpdateWindow
-
- This is our response to receipt of an update event for
- gMainWindow. It redraws our main window, respecting the
- current clip region, etc.
- */
- void UpdateWindow(WindowPtr theWindow)
- {
- GrafPtr savedPort; /* Used to temporarily save cur port */
- Rect windowRect; /* Rect of entire window */
- UnsignedWide startTime, endTime;
-
- GetPort(&savedPort); /* Save current port */
- SetPort(theWindow); /* Work in the specified window */
- BeginUpdate(theWindow);
-
- Microseconds(&startTime);
-
- LockPixels(gOffscreenPixMap);
-
- if (gFractalChanged) {
- GDHandle curGDevice;
-
- curGDevice = GetGDevice();
- SetGWorld((CGrafPtr)gOffscreenGWorld, NULL);
- SetRect(&windowRect, 0, 0, kNewScreenX, kNewScreenY);
- EraseRect(&windowRect);
- PlotData(gPlotSurface); /* Redraw contents of window */
- SetGDevice(curGDevice);
- }
-
- SetPort(gMainWindow);
- CopyBits((BitMap*)*gOffscreenGWorld->portPixMap,
- &gMainWindow->portBits,
- &(*gOffscreenGWorld->portPixMap)->bounds,
- &gMainWindow->portRect,
- srcCopy,
- NULL);
- BlastData(); //•• ES
- UnlockPixels(gOffscreenPixMap);
-
- /* If this is an update in response to a recomputation, we will count it
- as a part of the total time for the current fractal. If the update is
- in response to something else (e.g. the window coming to the front),
- we won’t time it. */
- if (gFractalChanged) {
- gFractalChanged = false;
- Microseconds(&endTime);
- if (startTime.hi == endTime.hi)
- gMicrosecondCount += endTime.lo - startTime.lo;
- else
- gMicrosecondCount += startTime.lo - endTime.lo;
- }
-
- DrawTimeInfo(); /* Draw performance statistics */
-
- SetPort(savedPort); /* Restore previous port */
- EndUpdate(theWindow);
- }
-
-
- /*
- DrawTimeInfo
- */
- void DrawTimeInfo(void)
- {
- Str255 tempString; /* Used for creating statistics string */
- Rect tempRect; /* Rect to erase */
-
- TextFont(geneva);
- TextSize(12);
- MoveTo(20, gMainWindowHeight - 5);
- DrawString("\pFractals per second: ");
-
- if (gMicrosecondCount != 0) { /* Make sure we don’t divide by zero */
- SetRect(&tempRect, 150, gMainWindowHeight - 20, 200, gMainWindowHeight);
- EraseRect(&tempRect);
- sprintf((char*)tempString, "%3.3f",
- (double)gTotalFractals * 1000000 / (double)gMicrosecondCount);
- c2pstr((char*)tempString);
- DrawString(tempString);
- }
- }
-
-
- /*
- ClearPointArrays
- */
- void ClearPointArrays(void)
- {
- long curX, curY;
-
- for (curX = 0; curX < kMaxXPoint; curX++) {
- for (curY = 0; curY < kMaxYPoint; curY++) {
- (*gOldPointArray)[curX][curY] = 0;
- (*gNewPointArray)[curX][curY] = 0;
- }
- }
- }
-
-
- /*
- ReportFatalError
-
- This function displays a fatal error alert and terminates the program.
- */
- void ReportFatalError(void)
- {
- (void) Alert(kFatalErrorAlertID, NULL);
- CleanUpApp();
- }
-
-
-
-
-
-
-